﻿#pragma once

#include <learn/geom/geom_nonbasis.h>
#include <learn/utils/sfinae.h>

#include <functional>

namespace xi
{

namespace geom
{

enum class ConicType
{
    Ellipse,
    Hyperbola,
    Parabola
};

struct EllipseFuncs
{
    static constexpr ConicType type = ConicType::Ellipse;

    template <std::size_t N = 0> double fx(double u) const
    {
        return fy<N + 1>(u);
    }

    template <std::size_t N = 0> double fy(double u) const
    {
        if constexpr (N % 4 == 0)
            return std::sin(u);
        else if constexpr (N % 4 == 1)
            return std::cos(u);
        else if constexpr (N % 4 == 2)
            return -std::sin(u);
        else
            return -std::cos(u);
    }
};

struct HyperbolaFuncs
{
    static constexpr ConicType type = ConicType::Hyperbola;

    template <std::size_t N = 0> double fx(double u) const
    {
        if constexpr (N % 2 == 0)
            return -std::cosh(u);
        else
            return -std::sinh(u);
    }

    template <std::size_t N = 0> double fy(double u) const
    {
        return fx<N + 1>(u);
    }
};

struct ParabolaFuncs
{
    static constexpr ConicType type = ConicType::Parabola;

    template <std::size_t N = 0> double fx(double u) const
    {
        if constexpr (N == 0)
            return u * u;
        else if constexpr (N == 1)
            return 2 * u;
        else if constexpr (N == 2)
            return 2;
        else
            return 0;
    }

    template <std::size_t N = 0> double fy(double u) const
    {
        return fx<N + 1>(u);
    }
};

template <std::size_t N, class Funcs> struct Geom_Conic : public Geom_NonbasisCurve<N>
{
    using Vec = VecT<N, double>;
    static constexpr Funcs funcs{};

  protected:
    Vec O, X, Y; // 平面坐标系

  public:
    Geom_Conic(Geom_Conic &&) = delete;

    /**
     * @brief 对于椭圆和双曲线，O 为中心，X 为主方向，Y 为副方向
     * 对于抛物线，O 为顶点，X 为对称轴，Y 为顶点切向
     *
     * @param[in] O
     * @param[in] X
     * @param[in] Y
     */
    Geom_Conic(const Vec &O, const Vec &X, const Vec &Y) : O(O), X(X), Y(Y)
    {
    }

    /**
     * @brief 计算曲线上点的位置
     *
     * @param[in] t
     * @return Vec
     */
    Vec d0(double t) const override
    {
        return O + X * funcs.fx(t) + Y * funcs.fy(t);
    }

    /**
     * @brief 计算曲线上点的位置和导数
     *
     * @param[in] t
     * @return std::array<Vec, 2>
     */
    std::array<Vec, 2> d1(double t) const override
    {
        return {O + X * funcs.fx(t) + Y * funcs.fy(t), X * funcs.fx<1>(t) + Y * funcs.fy<1>(t)};
    }

    /**
     * @brief 计算曲线上点的位置、导数和二阶导数
     *
     * @param[in] t
     * @return std::array<Vec, 3>
     */
    std::array<Vec, 3> d2(double t) const override
    {
        return {O + X * funcs.fx(t) + Y * funcs.fy(t), X * funcs.fx<1>(t) + Y * funcs.fy<1>(t),
                X * funcs.fx<2>(t) + Y * funcs.fy<2>(t)};
    }

    /**
     * @brief 计算曲线上点的位置、导数、二阶导数和三阶导数
     *
     * @param[in] t
     * @return std::array<Vec, 4>
     */
    std::array<Vec, 4> d3(double t) const override
    {
        return {O + X * funcs.fx(t) + Y * funcs.fy(t), X * funcs.fx<1>(t) + Y * funcs.fy<1>(t),
                X * funcs.fx<2>(t) + Y * funcs.fy<2>(t), X * funcs.fx<3>(t) + Y * funcs.fy<3>(t)};
    }

    /**
     * @brief 起始参数
     *
     * @return double
     */
    double first_param() const override
    {
        return -std::numeric_limits<double>::infinity();
    }

    /**
     * @brief 终止参数
     *
     * @return double
     */
    double last_param() const override
    {
        return std::numeric_limits<double>::infinity();
    }

    /**
     * @brief 序列化
     *
     * @return utils::Json
     */
    utils::Json dump_json() const override
    {
        utils::Json json;
        json["type"] = utils::enum_name(Funcs::type);
        json["O"] = O;
        json["X"] = X;
        json["Y"] = Y;
        return json;
    }
};

template <std::size_t N> using Geom_Ellipse = Geom_Conic<N, EllipseFuncs>;
template <std::size_t N> using Geom_Hyperbola = Geom_Conic<N, HyperbolaFuncs>;
template <std::size_t N> using Geom_Parabola = Geom_Conic<N, ParabolaFuncs>;

} // namespace geom

} // namespace xi